package com.jfs.security.resolver;

import cn.hutool.core.bean.BeanUtil;
import com.jfs.entity.Role;
import com.jfs.entity.User;
import com.jfs.security.annotations.LoginUser;
import com.jfs.security.properties.SecurityProperties;
import com.jfs.security.service.RoleService;
import com.jfs.security.service.UserService;
import com.jfs.security.utils.JwtTokenUtils;
import com.jfs.security.vo.LoginUserEntity;
import io.jsonwebtoken.Claims;
import org.springframework.core.MethodParameter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;

/**
 * @Description 获取当前用户信息LoginUser注解参数解析器
 * @E-mail cwm3412638@163.com
 * @Author cwm
 * @Date 2022/8/8
 */
public class LoginUserResolver extends AbstractMessageConverterMethodArgumentResolver {
    private final SecurityProperties securityProperties;
    private final UserService userService;
    private final RoleService roleService;

    public LoginUserResolver(List<HttpMessageConverter<?>> converters, SecurityProperties securityProperties, UserService userService, RoleService roleService) {
        super(converters);
        this.securityProperties = securityProperties;
        this.userService = userService;
        this.roleService = roleService;
    }

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(LoginUser.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        //参数中带有LoginUser户注解进行解析
        if (parameter.hasParameterAnnotation(LoginUser.class)) {
            //获取注解
            LoginUser parameterAnnotation = parameter.getParameterAnnotation(LoginUser.class);
            //使用缓存 直接解析请求中token进行转换
            String tokenForRequest = JwtTokenUtils.getTokenForRequest();
            //token不能失效  失效token不解析
            if (Objects.nonNull(tokenForRequest) && !JwtTokenUtils.isTokenExpired(tokenForRequest, securityProperties.getSecret())) {
                Claims claimsFromToken = JwtTokenUtils.getClaimsFromToken(tokenForRequest, securityProperties.getSecret());
                Class<?> parameterType = parameter.getParameterType();
                Object o = parameterType.newInstance();
                //参数是指定的loginUserEntity类型进行转换
                if (parameterType.isAssignableFrom(LoginUserEntity.class)) {
                    if (parameterAnnotation.isCache()) {
                        //使用缓存 直接解析请求中token进行转换
                        return setObjecFeild(parameterType, o,
                                String.valueOf(claimsFromToken.get(JwtTokenUtils.ID)),
                                String.valueOf(claimsFromToken.get(JwtTokenUtils.ROLE_ID)),
                                String.valueOf(claimsFromToken.get(JwtTokenUtils.PHONE)),
                                claimsFromToken.getSubject(),
                                String.valueOf(claimsFromToken.get(JwtTokenUtils.NICK_NAME)),
                                BeanUtil.toBean(claimsFromToken.get(JwtTokenUtils.ROLE), Role.class));
                    } else {
                        User user = this.userService.getById(String.valueOf(claimsFromToken.get(JwtTokenUtils.ID)));
                        if (Objects.nonNull(user)) {
                            Role role = this.roleService.getById(user.getRoleId());
                            return setObjecFeild(parameterType, o, user.getId(), user.getRoleId(), user.getPhone(), user.getUsername(),
                                    user.getNickName(), role);
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * 反射赋值
     * @param parameterType
     * @param o
     * @param id
     * @param roleId
     * @param phone
     * @param username
     * @param nickName
     * @param role
     * @return
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    private Object setObjecFeild(Class<?> parameterType, Object o, String id, String roleId, String phone, String username, String nickName, Role role) throws NoSuchFieldException, IllegalAccessException {
        //id
        Field idField = parameterType.getDeclaredField(JwtTokenUtils.ID);
        idField.setAccessible(true);
        idField.set(o, id);
        //roleId
        Field roleIdField = parameterType.getDeclaredField(JwtTokenUtils.ROLE_ID);
        roleIdField.setAccessible(true);
        roleIdField.set(o, roleId);
        //username
        Field usernameField = parameterType.getDeclaredField(JwtTokenUtils.USER_NAME);
        usernameField.setAccessible(true);
        usernameField.set(o, username);
        //phone
        Field phoneField = parameterType.getDeclaredField(JwtTokenUtils.PHONE);
        phoneField.setAccessible(true);
        phoneField.set(o, phone);
        //nickName
        Field nickNameField = parameterType.getDeclaredField(JwtTokenUtils.NICK_NAME);
        nickNameField.setAccessible(true);
        nickNameField.set(o, nickName);
        //role
        Field roleField = parameterType.getDeclaredField(JwtTokenUtils.ROLE);
        roleField.setAccessible(true);
        roleField.set(o, role);
        return o;
    }
}
